package org.koyad.portal.tags;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.koyad.portal.annotations.TagCordinates;
import org.koyad.portal.annotations.TagType;
import org.koyad.portal.context.PistonContext;
import org.koyad.portal.exceptions.ErrorCodes;
import org.koyad.portal.exceptions.FrameworkException;
import org.koyad.portal.utils.LogUtils;
import org.xml.sax.Attributes;

/**
 * Super class for all the tags
 * 
 * @author sshailen
 * 
 */
public abstract class AbstractTag {

	private static final LogUtils LOGGER = LogUtils.getLogger(AbstractTag.class);

	protected List<AbstractTag> _children = new ArrayList<AbstractTag>();
	
	protected Map<Object, Object> _store = null;
	
	public Object getValue(Object name) {
		Object value = null;
		if(null != _store) {
			value =  _store.get(name);
		}
		
		if(null == value && null != _parent) {
			value = _parent.getValue(name);
		} 
		
		return value;
	}
	
	private boolean setValueInParent(Object name, Object value) {
		if(null != _parent._store.get(name)) {
			_parent._store.put(name, value);
			return true;
		} 
		return _parent.setValueInParent(name, value);
	}
	
	protected void setValue(Object name, Object value, boolean local) {
		if (local == true || !setValueInParent(name, value)) {
			if(null == _store) {
				_store = new HashMap<Object, Object>();
			}
			_store.put(name, value);
		}
	}

	public void addChild(AbstractTag child) {
		_children.add(child);
	}

	protected AbstractTag _parent = null;

	public AbstractTag getParent() {
		return _parent;
	}

	public void setParent(AbstractTag parent) {
		this._parent = parent;
	}

	protected List<Attribute> _atts = null;

	public void setAttributes(List<Attribute> atts) {
		if(null == _atts) {
			_atts = new ArrayList<Attribute>();
		}
		_atts.addAll(atts);
	}
	
	public void adAttributes(Attribute att) {
		if(null == _atts) {
			_atts = new ArrayList<Attribute>();
		}
		_atts.add(att);
	}

	public List<Attribute> getAttributes() {
		return _atts;
	}
	
	public Attribute getAttribute(String name) {
		Attribute attr = null;
		for(Attribute att : _atts) {
			if(att.getName().equals(name)) {
				attr = att;
				break;
			}
		}
		return attr;
	}
	
	public String getAttributeValue(String name) {
		String value = null;
		for (int i = 0; i < _atts.size(); i++) {
			if(_atts.get(i).getName().equals(name)) {
				value = _atts.get(i).getValue();
			}
		}
		return value;
	}

	protected String _bodyText = "";

	public void setBodyText(String bodyText) {
		_bodyText = _bodyText + bodyText;
	}

	protected String _preStartTagText = "";

	public void setPreStartTagText(String preStartTagText) {
		_preStartTagText = preStartTagText;
	}

	protected String _preEndTagText = "";

	public void setPreEndTagText(String preEndTagText) {
		_preEndTagText = preEndTagText;
	}

	// private boolean _insideTag = false;
	// public boolean isInsideTag() {
	// return _insideTag;
	// }
	//
	// public void setInsideTag(boolean insideTag) {
	// _insideTag = insideTag;
	// }

	public void postInit() {

	}

	public void render() {
		PistonContext.getContext().getViewRoot().setCurrentTag(this);
		
		startTag();
		renderBody();
		closeTag();
		
		if(null != this._parent) {
			PistonContext.getContext().getViewRoot().setCurrentTag(this._parent);
		}
	}

	protected void startTag() {
		
		// get tag name
		TagCordinates cordinates = getClass()
				.getAnnotation(TagCordinates.class);
		String tagName = cordinates.localName();

		StringBuffer buffer = new StringBuffer(_preStartTagText + "<");
		buffer.append(tagName);

		if (!tagName.equals("html")) {
			writeAttributes(buffer);
		}

		if(!cordinates.type().equals(TagType.NOBODY)) {
			buffer.append(">");
		}

		buffer.append(_bodyText);
		
		getWriter().write(buffer.toString());
	}
	
	protected void renderBody() {
		for (AbstractTag tagBase : _children) {
			tagBase.render();
		}
	}

	/*
	 * Since <t> end tag need not to write anything to response, we need to
	 * provide blank implementation of this method in this class.
	 */
	protected void closeTag() {
		TagCordinates cordinates = getClass()
				.getAnnotation(TagCordinates.class);
		String tagName = cordinates.localName();
		if(!cordinates.type().equals(TagType.NOBODY)) {
			getWriter().write(_preEndTagText + "</" + tagName + ">");
		} else {
			getWriter().write(" //");
			getWriter().write(">");
		}
	}

	protected PrintWriter getWriter() {
		// get writer
		PistonContext pistonCtx = PistonContext.getContext();
		PrintWriter out = null;
		try {
			out = pistonCtx.getResponse().getWriter();
		} catch (IOException e) {
			LOGGER.logException(e);
			throw new FrameworkException(ErrorCodes.FWKRV002);
		}
		return out;
	}

	protected void writeAttributes(StringBuffer buffer) {
		for (Attribute att : _atts) {
			if(!att.getName().equalsIgnoreCase("schemaLocation")) {
				buffer.append(" ");
				buffer.append(att.getName());
				buffer.append("=\"");
				buffer.append(att.getValue());
				buffer.append("\"");
			}
		}
	}

	protected void validate() {
//		for (BaseTag tagBase : _children) {
//			tagBase.validate();
//		}
	}
}
